package com.aleyn.router.plug.visitor

import com.aleyn.router.plug.data.HandleModel
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.commons.InstructionAdapter


/**
 * @author: Aleyn
 * @date: 2023/7/20 15:06
 *
 * ASM MethodVisitor, 用于向 `LRouterGenerateImpl.init` 方法中注入字节码。
 *
 * 这个类的作用是将在扫描阶段找到的所有 `Initializer`（初始化器）注册到 `RouterController` 中。
 * 它遍历所有实现了 `LRouterInitializer` 接口并被 `@Initializer` 注解标记的类，
 * 然后为每个类生成调用 `RouterController.registerInitializer` 方法的字节码。
 *
 * 此 Adapter 会在 `LRouterGenerateImpl.init(Context context)` 方法中生成如下等效代码：
 * ```java
 * // 假设有一个 Initializer: com.example.MyInitializer, priority=10, async=true
 * RouterController.registerInitializer(10, true, new com.example.MyInitializer());
 * // ... 对其他 Initializer 重复此操作
 * ```
 *
 * @param api ASM API的版本。
 * @param methodVisitor 委托的下一个 MethodVisitor。
 * @param initializerClass 包含所有需要注册的初始化器信息的列表。
 */
class InitializerInstructAdapter(
    api: Int,
    methodVisitor: MethodVisitor,
    private val initializerClass: List<HandleModel.Initializer>?
) : InstructionAdapter(api, methodVisitor) {

    /**
     * 在目标方法的代码开始处被调用。
     *
     * 这里是实现字节码注入的关键。
     * 它会遍历 `initializerClass` 列表，为每个初始化器生成相应的注册代码。
     */
    override fun visitCode() {
        initializerClass?.forEach {
            // 加载优先级 (priority) 到操作数栈
            iconst(it.priority)
            // 加载是否异步 (async) 标志到操作数栈 (1 for true, 0 for false)
            mv.visitIntInsn(Opcodes.BIPUSH, it.async)
            // 创建 Initializer 实例: new com.example.MyInitializer()
            mv.visitTypeInsn(Opcodes.NEW, it.className)
            dup()
            // 调用其无参构造函数: <init>()
            invokespecial(it.className, "<init>", "()V", false)
            // 类型转换为 LRouterInitializer
            mv.visitTypeInsn(Opcodes.CHECKCAST, "com/aleyn/router/core/LRouterInitializer")
            // 调用静态方法 RouterController.registerInitializer(priority, async, instance)
            invokestatic(
                "com/aleyn/router/core/RouterController",
                "registerInitializer",
                "(BZLcom/aleyn/router/core/LRouterInitializer;)V",
                false
            )
        }
        super.visitCode()
    }

}